package 多线程.Test;

/**
 * @program: yc119_threadnet
 * @description: 生产者消费者程序
 * @author: zy
 * @create: 2022-12-07 20:22
 */
public class Test07 {
    public static void main(String[] args) {
        AppleBox ab=new AppleBox();   //  -> 程序

        Producer p1=new Producer( ab  );   // 5
        new Thread( p1) .start();

        Consumer c1=new Consumer(  ab );       // 4
        new Thread( c1 ).start();
        Consumer c2=new Consumer(  ab );    //1
        new Thread( c2 ).start();

    }

}

//生产者
class Producer implements Runnable{
    AppleBox ab=null;
    Producer( AppleBox ab){
        this.ab=ab;
    }
    @Override
    public void run() {
        //生成消息存到  appleBox
        for( int i=0;i<5;i++){  //死循环
            Apple a=new Apple( i );
            ab.deposite(  a ); //生产消息存到 容器中
            System.out.println(Thread.currentThread().getName()+"生产了:"+ a );
            try {
                Thread.sleep(   (int) (Math.random()*50 ));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//消费
class Consumer implements Runnable{
    AppleBox ab=null;
    Consumer( AppleBox ab){
        this.ab=ab;
    }
    @Override
    public void run() {
        //从appleBox取消息
        for( int i=0;i<5;i++){  //死循环
            Apple a=ab.withdraw(   ); //到容器中取消息
            System.out.println(Thread.currentThread().getName()+"消费了******:"+ a );
            try {
                Thread.sleep(   (int) (Math.random()*1000 ));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//队列其实就是一个集合，存这个消息(Apple)
//1. 引入同步操作
//2. 假设生产满了， 怎么办. -> 还能生产吗, 不能》 停(   sleep/wait  )
//3.                如果此时有消费，则生产端是否是要继续.....
//中间件
class AppleBox {
    private static String lock = "lock";
    int index = 0;  //消息的指针.
    Apple[] apples = new Apple[5];   // 消息中间件:  TODO: ->   1. 引入消息队列，保证先进先出   2. 容量大小   3. 消息权重机制.
    //4.重复? 记录消费的位置  -> 流   5. 持久化    6. 存多久，存多少.   7. 吞吐量( 多个服务器存, xxxx ,负载均衡 )

    //      同步  synchronized
    //
    //存消息
    public  void deposite(   Apple apple )  {
        synchronized(lock){
            //满 +锁
            /*
            * 1.为什么index==apples.length（5），而不是4，这个条件如果成立只是说当前刚好放进去！后！才满，
            * 如果是4的就是在放进去就加锁wait了，所以始终至多4个苹果放进去
            * 2.为什么wait不用sleep，sleep不会释放当前锁，设置一个等待时间不太确定时间是多少
            * 3.wait设置时间同理
            * 4.wait不设置时间需要notifyAll唤醒其他进程来消费/生产才能避免死锁，否则一直处于while循环中
            * */
            while(index==apples.length){
                try {
                    lock.wait();
//                    lock.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //没满，存进去
            apples[index] = apple;
            index++;
            lock.notifyAll();//唤醒其他等待的进程，但是只是进入就绪状态，因为这个锁还没有释放
        }

    }
    //取消息
//    public synchronized  Apple withdraw(  ){
    public   Apple withdraw(  ){
        synchronized(lock){
            //空 +锁
            while(index==0){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //没空，取
            index--;
            lock.notifyAll();
        }

        return apples[index];
    }
}

//待处理的消息
class Apple {
    int id;
    Apple(int id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "apple " + id;
    }
}
